package com.google.code.yargon;

import java.util.HashMap;

import com.google.code.yargon.level.*;
import com.google.code.yargon.ui.*;
import com.google.code.yargon.util.*;
import com.google.code.yargon.engine.AI;
import com.google.code.yargon.engine.Player;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.*;
import org.apache.log4j.Logger;

/**
 * 
 * Main class, or a launcher
 * 
 * @author Mika Myllynen, Tuomas Oinonen
 *
 */
public class Yargon {

	// The game instance is static, so we can access everything the game holds by calling
	// Yargon.getInstance().getCharacter for example
	private static Yargon instance = null;
	
	// In the app entry point props & logger should be instanced in this order for everything to work.
	// Later on, if only logger is needed there is no need for props object. See MapGenerator.java for example.
	private PropertyUtil props = PropertyUtil.getInstance();
	private Logger log = LogUtil.getInstance();
	
	// Player character
	private Player pc = null;
	
	// List of levels
	//private List<LevelArea> levels = null;
	
	// Tile sets
	private HashMap<String, TileSet> tileSets = null;
	private String currentTileSet = TileSet.TILESET_WARPED;
	
	// Window and UI properties
	private int windowWidth = props.getInt("yargon.window.width");
	private int windowHeight = props.getInt("yargon.window.height");
	private int uiWidth = props.getInt("yargon.window.ui.width");
	private int uiHeight = props.getInt("yargon.window.ui.height");
	
	// Engine components
	private RenderEngine re = null;
	private InputHandler ih = null;
	private LevelManager lm = null; 
	private AI ai = null;
	
	private boolean developerMode = false;
	
	/**
	 * Main
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		Yargon yargon = getInstance();
		yargon.run(args);
	}
	
	/**
	 * Constructor
	 * 
	 */
	private Yargon() {
	}
	
	/**
	 * Get game instance
	 * 
	 */
	public static Yargon getInstance() {
		if(null == instance) {
			instance = new Yargon();
		} 
		return instance;
	}
	
	/**
	 * Get the player character
	 * 
	 * @return Character
	 */
	public Player getPlayerCharacter() {
		return this.pc;
	}
	
	/**
	 * Get tile set
	 * 
	 * @param tileset
	 * @return Level
	 */
	public TileSet getLevel(String tileset) {
		return tileSets.get(tileset);
	}
	
	/**
	 * Get current tile set
	 * 
	 * @return Level
	 */
	public TileSet getCurrentTileSet() {
		return tileSets.get(currentTileSet);
	}
	
	/**
	 * Get app window width
	 * 
	 * @return
	 */
	public int getWindowWidth() {
		return this.windowWidth;
	}
	
	/**
	 * Get app window height
	 * 
	 * @return
	 */
	public int getWindowHeight() {
		return this.windowHeight;
	}
	
	/**
	 * Get UI width
	 * 
	 * @return
	 */
	public int getUIWidth() {
		return this.uiWidth;
	}
	
	/**
	 * Get UI height
	 * 
	 * @return
	 */
	public int getUIHeight() {
		return this.uiHeight;
	}
	
	/**
	 * Return true if developer mode is set
	 * @return
	 */
	public boolean getDeveloperMode() {
		return this.developerMode;
	}
	
	/**
	 * Set developer "cheat" mode
	 * 
	 * @param m
	 */
	public void setDeveloperMode(boolean m) {
		this.developerMode = m;
	}
		
	/**
	 * Run the game
	 * 
	 * @param args
	 * @return
	 */
	private int run(String[] args) {
		
		long lastFrame = 0;
		long fpsCap = props.getInt("yargon.window.maxfps");
		
		log.info("Starting");
					
		// Initialize render engine first so we can inform the player what is happening during initialization phase
		re = RenderEngine.getInstance();
		re.initFirstPhase();
		re.message("Yargon v. "+props.getString("yargon.version")+" initializing...");
		re.display();
						
		// Initialize game components: Player
		pc = new Player();
			
		// Tile sets
		tileSets = new HashMap<String, TileSet>();	
		tileSets.put(TileSet.TILESET_WARPED, new TileSet(TileSet.TILESET_WARPED));
		//tileSets.put(TileSet.TILESET_SHADOW, new TileSet(TileSet.TILESET_SHADOW)); FIXME: Temporarily disabled, texture map not compiled yet
			
		// Inputs
		ih = InputHandler.getInstance();
		
		// AI
		ai = AI.getInstance();
		
		// Level generation
		re.message("  Generating level...");
		re.display();
		lm = LevelManager.getInstance();
		lm.add(new LevelArea(100, 100));
		lm.generate(0);
		
		// Player character initial update, also calculates vision map
		re.message("  Preparing player character...");
		re.display();
		pc.getLevelState().update(pc.getLocation(), lm.get(0));
		
		// Now that we have all the components, we can finish render engine initialization (creates map component)
		re.initSecondPhase();
		re.message("Welcome!");
						
		// Main loop
		while(! Display.isCloseRequested()) {
				
			// Handle inputs
			Mouse.poll();
			Keyboard.poll();
			if(ih.handleInput()) {
			
				// Update AI if player moved
				ai.update();
			}
					
			// Do all the drawing inside this if-block (this limits FPS to the allowed cap)
			if(YargonUtil.getTime() - lastFrame > (float)(1000 / fpsCap)) {
				re.display();		
				lastFrame = YargonUtil.getTime();														
			}			
		}		
		Display.destroy();	
		return 0;
	}
}
